package com.zhongyouyun.other.aspect;

import cn.hutool.core.bean.BeanUtil;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.zhongyouyun.admin.mapper.LogMapper;
import com.zhongyouyun.admin.model.LogModel;
import com.zhongyouyun.admin.model.UserModel;
import com.zhongyouyun.utils.IPUtils;
import io.swagger.annotations.Authorization;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.subject.Subject;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import com.zhongyouyun.other.annotation.SysLogAnnotation;
import com.alibaba.fastjson.JSONObject;

import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletRequest;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author hewenjun
 * @title: OperationAspect
 * @date 2021/9/23
 * @description: 操作日志切面处理类
 */
@Aspect
@Component
@Slf4j
@Transactional
public class SysLogAspect {
    @Autowired
    LogMapper logMapper;

    /**
     * 设置操作日志切入点   在注解的位置切入代码
     */
    @Pointcut("@annotation(com.zhongyouyun.other.annotation.SysLogAnnotation)")
    public void operLogPoinCut() {
    }

    /**
     * 记录操作日志
     *
     * @param joinPoint 方法的执行点
     * @param result    方法返回值
     * @throws Throwable
     */
    @AfterReturning(returning = "result", value = "operLogPoinCut()")
    public void saveOperLog(JoinPoint joinPoint, Object result) throws Throwable {
        // 获取RequestAttributes
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        // 从获取RequestAttributes中获取HttpServletRequest的信息
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);
        String xiangmuType = request.getHeader("xiangmuType");
        try {
            LogModel sysLog = new LogModel();
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            //获取切入点所在的方法
            Method method = signature.getMethod();
            //获取操作
            SysLogAnnotation annotation = method.getAnnotation(SysLogAnnotation.class);
            if (annotation != null) {
                sysLog.setModel(annotation.operModul());
                sysLog.setType(annotation.operType());
                sysLog.setDescription(annotation.operDesc());
            }

            // 获取请求的类名
            String className = joinPoint.getTarget().getClass().getName();
            // 获取请求的方法名
            String methodName = method.getName();
            methodName = className + "." + methodName;
            sysLog.setMethod(methodName); // 类名.请求方法

            sysLog.setCreateDate(new Date()); //操作时间
            //操作用户 --登录时有把用户的信息保存在session中，可以直接取出
            Subject subject = SecurityUtils.getSubject();
            Object u = SecurityUtils.getSubject().getPrincipal();
            if (u instanceof UserModel) {
                UserModel userModel = (UserModel) SecurityUtils.getSubject().getPrincipal();
                if (ObjectUtil.isNotNull(userModel)) {
                    sysLog.setUserId(userModel.getId().toString());
                    sysLog.setDeptId(userModel.getDeptId());
                    sysLog.setXiangMuType(xiangmuType);
                    sysLog.setCreateBy(userModel.getId().toString());
                    sysLog.setTrueName(userModel.getTrueName());
                    sysLog.setIp(IPUtils.getIpAddr(request)); //操作IP IPUtils工具类网上大把的，比如工具类集锦的hutool.jar
                    sysLog.setUrl(request.getRequestURI()); // 请求URI


                    // 方法请求的参数
                    Map<String, Object> rtnMap = getFieldsName(joinPoint);
                    // 将参数所在的数组转换成json
                    String params = JSONUtil.toJsonStr(rtnMap);
                    //获取json的请求参数
                    if (rtnMap == null || rtnMap.size() == 0) {
                        params = getJsonStrByRequest(request);
                    }
                    sysLog.setParams(params); // 请求参数


                    Map<String, Object> dataResult = BeanUtil.beanToMap(result);
                    ;  //返回值信息
                    //需要先判断返回值是不是Map <String, Object>，如果不是會拋異常，需要控制层的接口返回数据格式统一
                    //如果嫌返回格式统一太麻烦建议日志保存时去掉操作结果
                    sysLog.setResult(JSONUtil.toJsonStr(dataResult)); //獲取方法返回值中的msg，如果上面的類型錯誤就拿不到msg就會拋異常

                    //保存日志
//            sysLog.setId(NanoId.randomNanoId());
                    logMapper.insert(sysLog);
                }
            } else if (u instanceof UserModel) {
               //这里可能是手机端的用户
            }
        } catch (Exception e) {
            e.printStackTrace();
            log.error("日誌記錄異常，請檢查返回值是否是Map <String, Object>類型");
        }
    }

    /**
     * 转换request 请求参数
     *
     * @param joinPoint joinPoint 获取的请求类
     */
    public Map<String, Object> converMap(JoinPoint joinPoint) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        Authorization authCode = method.getAnnotation(Authorization.class);
        Object[] args = joinPoint.getArgs();

        Map<String, Object> fieldsName = getFieldsName(joinPoint);
        return fieldsName;
    }

    /**
     * 获取参数列表
     *
     * @param joinPoint
     * @return
     * @throws ClassNotFoundException
     * @throws NoSuchMethodException
     */
    public Map<String, Object> getFieldsName(JoinPoint joinPoint) {
        // 参数值
        Object[] args = joinPoint.getArgs();
        ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String[] parameterNames = pnd.getParameterNames(method);
        Map<String, Object> paramMap = new HashMap<>(32);
        for (int i = 0; i < parameterNames.length; i++) {
            paramMap.put(parameterNames[i], args[i]);
        }
        return paramMap;
    }


    /**
     * 获取json格式 请求参数
     */
    public String getJsonStrByRequest(HttpServletRequest request) {
        String param = null;
        try {
            BufferedReader streamReader = new BufferedReader(new InputStreamReader(request.getInputStream(), "UTF-8"));
            StringBuilder responseStrBuilder = new StringBuilder();
            String inputStr;
            while ((inputStr = streamReader.readLine()) != null) {
                responseStrBuilder.append(inputStr);
            }

            JSONObject jsonObject = JSONObject.parseObject(responseStrBuilder.toString());
            if (ObjectUtil.isNotEmpty(jsonObject)) {
                param = jsonObject.toJSONString();
            }
//            System.out.println(param);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return param;
    }


    /**
     * 转换异常信息为字符串
     *
     * @param exceptionName    异常名称
     * @param exceptionMessage 异常信息
     * @param elements         堆栈信息
     */
    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] elements) {
        StringBuffer strbuff = new StringBuffer();
        for (StackTraceElement stet : elements) {
            strbuff.append(stet + "\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + strbuff.toString();
        return message;
    }

}
